package sk.model;

import java.util.ArrayList;
import java.util.List;
import sk.database.Server;

public class Game {

	private int id;
	private int[] tariff;
	private GameType type;
	private List<PlayerData> players;
	private int player = -1;
	private int teamMate = -1;
	private int[] opponents;
	private int runners;
	private Schneider schneider;
	private int layers = 0;
	private int injections = 0;
	private boolean won;
	private long time;
	private int lastInjecter = -1;
	private boolean[] hasInjected = new boolean[4];

	public Game(int id, int[] tarif) {
		time = Server.getTime();
		setGameType(type);
		if (tarif.length == 3) {
			this.tariff = tarif;
		} else {
			throw new DaLaftWosFoischException("Tarif has wrong length.");
		}
	}

	public void setPlayers(List<Player> pl) {
		if (pl.size() != 4) {
			throw new DaLaftWosFoischException(
				"Number of participating players unequal four.");
		}
		players = new ArrayList<PlayerData>();
		for (int i = 0; i < 4; i++) {
			players.add(new PlayerData(pl.get(i)));
		}
	}

	public List<PlayerData> getPlayers() {
		return players;
	}

	public void setGameType(GameType type) {
		this.type = type;
		if (type == GameType.RAMSCH || type == GameType.MISDEAL) {
			schneider = Schneider.FREI;
			runners = 0;
			won = false;
		}
	}

	public void setPlayer(Player player) {
		if (type != null) {
			this.player = getIndexOfPlayer(player);
		} else {
			throw new DaLaftWosFoischException("Set game type first.");
		}
	}

	public void setOpponents(Player[] opponents) {
		if ((opponents.length == 3 && (type == GameType.SOLO
			|| type == GameType.WENZ || type == GameType.RAMSCH))
			|| (opponents.length == 2 && type == GameType.SAUSPIEL)
			|| (opponents.length == 3 && type == GameType.MISDEAL)
			|| (opponents.length == 4 && type == GameType.MISDEAL)) {
			int[] opp = new int[opponents.length];
			for (int i = 0; i < opponents.length; i++) {
				opp[i] = getIndexOfPlayer(opponents[i]);
			}
			this.opponents = opp;
		} else {
			throw new DaLaftWosFoischException("To much or to few opponents.");
		}
	}

	public void setTeamMate(Player teamMate) {
		if (type == GameType.SAUSPIEL) {
			this.teamMate = getIndexOfPlayer(teamMate);
		} else {
			throw new DaLaftWosFoischException(
				"Tried to set team mate but no Sauspiel.");
		}
	}

	public void setRunners(int runners) {
		if (runners == 0
			|| (type == GameType.WENZ && runners >= 2 && runners <= 4)
			|| (type == GameType.SOLO && runners >= 3 && runners <= 8)
			|| (type == GameType.SAUSPIEL && runners >= 3 && runners <= 14)) {
			this.runners = runners;
		} else {
			throw new DaLaftWosFoischException("Wrong number of runners.");
		}
	}

	public void setSchneider(Schneider schneider) {
		this.schneider = schneider;
	}

	public void setWon(boolean won) {
		this.won = won;
	}

	public void addInjection(Player p) {
		int index = getIndexOfPlayer(p);
		if (lastInjecter == index) {
			throw new DaLaftWosFoischException("Player " + p.getName()
				+ "  tried to injected two times in a row.");
		}
		lastInjecter = index;
		PlayerData data = players.get(index);
		data.addInjection();
		injections++;
	}

	public void addLayer(Player p) {
		PlayerData data = players.get(getIndexOfPlayer(p));
		data.setLayed(true);
		layers++;
	}

	public void setVirgin(Player p) {
		PlayerData data = players.get(getIndexOfPlayer(p));
		data.setVirgin(true);
	}

	private int getIndexOfPlayer(Player p) {
		try {
			int index = -1;
			for (int i = 0; i < players.size(); i++) {
				if (p.equals(players.get(i).getPlayer())) {
					index = i;
				}
			}
			return index;
		} catch (NullPointerException e) {
			throw new DaLaftWosFoischException(
				"Player who doesn't plays tried to participate.");
		}
	}

	public Player getTeamMate() {
		return players.get(teamMate).getPlayer();
	}

	public int getInjections() {
		return injections;
	}

	public Player[] getOpponents() {
		Player[] opp = new Player[opponents.length];
		for (int i = 0; i < opp.length; i++) {
			opp[i] = players.get(opponents[i]).getPlayer();
		}
		return opp;
	}

	public GameType getType() {
		return type;
	}

	public int getRunners() {
		return runners;
	}

	public int getLayers() {
		return layers;
	}

	public int[] getTariff() {
		return tariff;
	}

	public Schneider getSchneider() {
		return schneider;
	}

	public Player getPlayer() {
		return players.get(player).getPlayer();
	}

	public boolean isWon() {
		return won;
	}

	public long getTime() {
		return time;
	}

	public Game(String string) {
		String[] tokens = string.split("%");
		tariff = new int[3];
		String[] tariffTokens = tokens[0].split("\\?");
		tariff[0] = Integer.parseInt(tariffTokens[0]);
		tariff[1] = Integer.parseInt(tariffTokens[1]);
		tariff[2] = Integer.parseInt(tariffTokens[2]);
		setGameType(GameType.valueOf(tokens[1]));
		player = Integer.parseInt(tokens[2]);
		if (tokens[3].length() > 0) {
			teamMate = Integer.parseInt(tokens[3]);
		}
		String[] opponentTokens = tokens[4].split("\\?");
		int opponentSize = Integer.parseInt(opponentTokens[0]);
		opponents = new int[opponentSize];
		for (int i = 0; i < opponentSize; i++) {
			opponents[i] = Integer.parseInt(opponentTokens[i + 1]);
		}
		runners = Integer.parseInt(tokens[5]);
		if (type != GameType.RAMSCH) {
			schneider = Schneider.valueOf(tokens[6]);
		}
		layers = Integer.parseInt(tokens[7]);
		injections = Integer.parseInt(tokens[8]);
		won = Boolean.parseBoolean(tokens[9]);
		time = Long.parseLong(tokens[10]);
		id = Integer.parseInt(tokens[11]);
		players = new ArrayList<PlayerData>();
		int numberOfPlayers = opponents.length + 1;
		if (type == GameType.SAUSPIEL) {
			numberOfPlayers++;
		}
		for (int i = 0; i < numberOfPlayers; i++) {
			players.add(new PlayerData(tokens[12 + i]));
		}
	}

	public String toString() {
		String returnString = "";
		returnString += tariff[0] + "?" + tariff[1] + "?" + tariff[2];
		returnString += "%" + type.name();
		returnString += "%" + player + "%";
		if (teamMate != -1) {
			returnString += teamMate;
		}
		returnString += "%";
		returnString += opponents.length + "?" + opponents[0];
		for (int i = 1; i < opponents.length; i++) {
			returnString += "?" + opponents[i];
		}
		returnString += "%" + runners;
		returnString += "%" + schneider.name();
		returnString += "%" + layers;
		returnString += "%" + injections;
		returnString += "%" + won;
		returnString += "%" + time;
		returnString += "%" + id;
		for (PlayerData pDat : players) {
			returnString += "%" + pDat.toString();
		}
		return returnString;
	}

	public int getPlayerResult() {
		int returnInt;
		if (type == GameType.SAUSPIEL) {
			int result =
				tariff[1] + tariff[0] * (runners + schneider.ordinal());
			returnInt = result * (int) Math.pow(2, layers + injections);
		} else if (type == GameType.RAMSCH) {
			returnInt = 0;
			for (int i = 0; i < opponents.length; i++) {
				returnInt += getResult(players.get(opponents[i]).getPlayer());
			}

		} else if (type == GameType.MISDEAL) {
			returnInt = 0;
			for (int i = 0; i < opponents.length; i++) {
				returnInt += getResult(players.get(opponents[i]).getPlayer());
			}

		} else {
			int result =
				tariff[2] + tariff[0] * (runners + schneider.ordinal());
			returnInt = 3 * result * (int) Math.pow(2, layers + injections);
		}
		if (won) {
			return returnInt;
		} else {
			return returnInt * -1;
		}
	}

	public int getOpponentResult() {
		int returnInt;
		if (type == GameType.SAUSPIEL) {
			int result =
				tariff[1] + tariff[0] * (runners + schneider.ordinal());
			returnInt = result * (int) Math.pow(2, layers + injections);
		} else if (type == GameType.RAMSCH) {
			returnInt =
				tariff[2]
					* (int) Math.pow(2, layers
						+ players.get(player).getInjected());
		} else {
			int result =
				tariff[2] + tariff[0] * (runners + schneider.ordinal());
			returnInt = result * (int) Math.pow(2, layers + injections);
		}
		if (won) {
			return returnInt * -1;
		} else {
			return returnInt;
		}
	}

	public int getResult(Player p) {
		if (p.equals(players.get(player).getPlayer())) {
			return getPlayerResult();
		}
		if (type == GameType.RAMSCH) {
			for (int i = 0; i < opponents.length; i++) {
				PlayerData pData = players.get(opponents[i]);

				if (p.equals(pData.getPlayer())) {
					return getOpponentResult()
						* (pData.getInjected() > 0 ? 2 : 1)
						* (pData.isVirgin() ? 2 : 1);
				}
			}
		} else if (type == GameType.SAUSPIEL
			&& p.equals(players.get(teamMate).getPlayer())) {
			return getPlayerResult();
		} else if (type == GameType.MISDEAL) {
			for (int i = 0; i < opponents.length; i++) {
				PlayerData pData = players.get(opponents[i]);

				if (p.equals(pData.getPlayer())) {
					return tariff[0] * (pData.isLayed() ? 2 : 1);
				}
			}
		}
		for (int i = 0; i < opponents.length; i++) {
			if (p.equals(players.get(opponents[i]).getPlayer())) {
				return getOpponentResult();
			}
		}
		return 0;
	}

	public int getId() {
		return id;
	}
}